﻿using System;
using System.Collections.Generic;
using System.Linq;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.GamerServices;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Media;

namespace arthuraafn_xna_3d.Base

{
    class GameObject3D:DrawableGameComponent 
    {
        protected Model model;

        //shaders:
        BasicEffect basicEffect;

        public Matrix world { get; set; }
        float rotationX;
        float rotationY;
        float rotationZ;
        float scaleX;
        float scaleY;
        float scaleZ;
        float scale;

        Vector3 position;
        float positionX;
        float positionY;
        float positionZ;

        protected BasicCamera camera;

        //Bones Transforms
        private Matrix[] boneTransforms;

        public GameObject3D(Game game, BasicCamera camera, Model model):base(game)
        {
            this.camera=camera;

            this.basicEffect = new BasicEffect(Game.GraphicsDevice);

            this.model = model;

            // inicializa matrix do modelo
            boneTransforms = new Matrix[model.Bones.Count];

            model.CopyAbsoluteBoneTransformsTo(boneTransforms);

            //valor padrão da escala do objeto
            Scale = 1;

            //inicializa rotação
            rotationX = rotationY = rotationZ = 0;

            //inicializa posição
            Position = Vector3.Zero;
            
            this.world = Matrix.Identity;

            UpdateWorld();

            game.Components.Add(this);
        }

        public void ModelTransform(Matrix T)
        {
            for (int i = 0; i < model.Bones.Count; i++)
            {
                boneTransforms[0] *= T;
            }
        }

        public override void Update(GameTime gameTime)
        {
           UpdateWorld();
        }

        protected void UpdateWorld()
        {
            this.world =
                Matrix.CreateRotationX(rotationX) *
                Matrix.CreateRotationY(rotationY) *
                Matrix.CreateRotationZ(rotationZ) *
                Matrix.CreateScale(scaleX, scaleY, scaleZ) *
                Matrix.CreateTranslation(position);
        }

        public override void Draw(GameTime gameTime)
        {
            //model.Draw(this.world, camera.ViewMatrix, camera.ProjectionMatrix);
            foreach (ModelMesh mesh in this.model.Meshes)
            {
                foreach (BasicEffect effect in mesh.Effects)
                {
                    //effect.World = this.world;
                    effect.World = boneTransforms[mesh.ParentBone.Index] * this.world;
                    effect.View = camera.ViewMatrix;
                    effect.Projection = camera.ProjectionMatrix;
                    effect.EnableDefaultLighting();
                    effect.PreferPerPixelLighting = true;

                }
                mesh.Draw();
            }
        }

        public bool CastRayCollision(Ray ray) 
        {
            bool collied = false;
            foreach (ModelMesh mesh in this.model.Meshes)
            {
                BoundingSphere sphere = mesh.BoundingSphere;
                sphere.Center += this.position;

                Nullable<float> result = ray.Intersects(sphere);

                if (result.HasValue)
                {
                    return true;
                }
                //foreach (BasicEffect effect in mesh.Effects)
               // {
                    //effect.World = this.world;
                    //effect.World = boneTransforms[mesh.ParentBone.Index] * this.world;

                //}
            }
            return collied;
        } 


        #region Propriedades da classe
        /// <summary>
        /// Propriedade para alterar a escala do objeto de forma global
        /// </summary>
        public float Scale {
            get 
            {
                return scale;
            }
        
            set
            {
                scale = value;
                scaleX = scaleY = scaleZ = scale;
            }

        }

        /// <summary>
        /// Propriedade angulo de rotação sobre o eixo X em radianos
        /// </summary>
        public float RotationX
        {
            get { return rotationX; }
            set { rotationX = value; }
        }

        /// <summary>
        /// Propriedade angulo de rotação sobre o eixo X em graus
        /// </summary>
        public float RotationXDegrees
        {
            get { return MathHelper.ToDegrees( rotationX); }
            set { rotationX = MathHelper.ToRadians(value); }
        }

        /// <summary>
        /// Propriedade angulo de rotação sobre o eixo Y
        /// </summary>
        public float RotationY
        {
            get { return rotationY; }
            set { rotationY = value; }
        }

        /// <summary>
        /// Propriedade angulo de rotação sobre o eixo Y em graus
        /// </summary>
        public float RotationYDegrees
        {
            get { return MathHelper.ToDegrees(rotationY); }
            set { rotationY = MathHelper.ToRadians(value); }
        }

        /// <summary>
        /// Propriedade angulo de rotação sobre o eixo Y
        /// </summary>
        public float RotationZ
        {
            get { return rotationZ; }
            set { rotationZ = value; }
        }

        /// <summary>
        /// Propriedade angulo de rotação sobre o eixo Z em graus
        /// </summary>
        public float RotationZDegrees
        {
            get { return MathHelper.ToDegrees(rotationZ); }
            set { rotationZ = MathHelper.ToRadians(value); }
        }
   
        /// <summary>
        ///Vetor da posição do objeto
        /// </summary>
        public Vector3 Position 
        {
            get { return position; }
            set 
            { 
                position = value;
                positionX = position.X;
                positionY = position.Y;
                positionZ = position.Z;
             }
        }

        /// <summary>
        /// Posição X
        /// </summary>
        public float PositionX
        {
            get { return positionX; }
            set 
            { 
                positionX = value;
                position.X = positionX;
            }
        }

        /// <summary>
        /// Posição Y
        /// </summary>
        public float PositionY
        {
            get { return positionY; }
            set
            {
                positionY = value;
                position.Y = positionY;
            }
        }

        /// <summary>
        /// Posição Z
        /// </summary>
        public float PositionZ
        {
            get { return positionZ; }
            set
            {
                positionZ = value;
                position.Z = positionZ;
            }
        }

        #endregion
    }
}

